#include "guish.hpp"

IOManager *ioManager = NULL;

IOUser::IOUser(int order)
{
	ioManager->ioUsers.insert(pair<int, IOUser*>(order, this));
}

IOUser::~IOUser()
{
	for(multimap<int, IOUser*>::iterator ii=ioManager->ioUsers.begin(); ii!=ioManager->ioUsers.end(); ii++)
	{
		if(ii->second == this) {
			ioManager->ioUsers.erase(ii);
			break;
		}
	}
}

//////////////////////////////////////////////////////////////////////////////

IOManager::IOManager(EventManager *eventManager)
{
	if(pipe(selfpipe) < 0)
		assert(0);
	setFileAsync(selfpipe[0], true);
	setFileAsync(selfpipe[1], true);
	setCloseOnExec(selfpipe[0], true);
	setCloseOnExec(selfpipe[1], true);
	
	eventManager->setNotifyPipe(selfpipe[1]);
}

IOManager::~IOManager()
{
	close(selfpipe[0]);
	close(selfpipe[1]);
}

// Priority levels:
//  1: Input from terminal, output to terminal, output to processes' stdins
//  2: Input from processes' stdouts
//  3: File transfers
bool IOManager::doIO(bool canBlock, int priority)
{
	int maxfd = 0;
	fd_set readfds;
	fd_set writefds;
	fd_set exceptfds;
	
	FD_ZERO(&readfds);
	FD_ZERO(&writefds);
	FD_ZERO(&exceptfds);
	
	for(multimap<int, IOUser*>::iterator ii=ioUsers.begin(); ii!=ioUsers.end(); ii++)
	{
		IOUser *ioUser = ii->second;
		ioUser->prepareSelect(priority, &readfds, &writefds, &exceptfds, &maxfd);
	}
	
	FD_SET(selfpipe[0], &readfds);
	if(selfpipe[0] > maxfd) maxfd = selfpipe[0];
	
	struct timespec zeroTimeout;
	zeroTimeout.tv_sec=0;
	zeroTimeout.tv_nsec=0;
	struct timespec *timeout = canBlock ? NULL : &zeroTimeout;
	
	int numReady = pselect(maxfd+1, &readfds, &writefds, &exceptfds, timeout, NULL);
	if(!numReady) return false;
	
	ptyManager->postSelect(priority, &readfds, &writefds, &exceptfds);
	
	if(FD_ISSET(selfpipe[0], &readfds)) {
		char buf[128];
		read(selfpipe[0], buf, 128);
	}
	
	Event *event;
	while((event = eventManager->nextEvent()))
	{
		handleEvent(event);
		delete event;
	}
	
	for(multimap<int, IOUser*>::iterator ii=ioUsers.begin(); ii!=ioUsers.end(); ii++)
	{
		IOUser *ioUser = ii->second;
		ioUser->postSelect(priority, &readfds, &writefds, &exceptfds);
	}
	
	return true;
}

